package com.avocado.base.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

import javax.persistence.criteria.Predicate;

import com.avocado.base.dto.user.*;
import com.avocado.base.enums.CertificateType;
import com.avocado.base.vo.SimpleUser;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import com.avocado.commons.utils.DateUtils;
import com.avocado.base.entity.Role;
import com.avocado.base.entity.User;
import com.avocado.base.entity.UserRole;
import com.avocado.base.entity.UserRoleId;
import com.avocado.base.enums.Gender;
import com.avocado.base.repository.OrganizationRepository;
import com.avocado.base.repository.RoleRepository;
import com.avocado.base.repository.UserRepository;
import com.avocado.base.repository.UserRoleRepository;
import com.avocado.base.service.UserService;

@Service
public class UserServiceImpl  implements UserService {

   private final UserRepository userRepository;


   private final OrganizationRepository organizationRepository;


   private final RoleRepository roleRepository;


   private final UserRoleRepository userRoleRepository;

	public UserServiceImpl(UserRepository userRepository, OrganizationRepository departmentRepository, RoleRepository roleRepository, UserRoleRepository userRoleRepository) {
		this.userRepository = userRepository;
		this.organizationRepository = departmentRepository;
		this.roleRepository = roleRepository;
		this.userRoleRepository = userRoleRepository;
	}

	private static final String DELETED_USER_NAME_FLAG="_deleted_";

   
   public static PasswordEncoder passwordEncoder=new BCryptPasswordEncoder();
   
   @Override
   public User getOne(Long id) {

		return userRepository.getOne(id);

   }
	
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor=Exception.class)
    public Long add(UserAddDto dto) throws Exception {

   	    User user =new User();

	    BeanUtils.copyProperties(dto, user);

	    setOtherAttribute(dto, user);

        setOrganization(dto,user);
      
        String encodedPassword= passwordEncoder.encode(user.getPassword());
    
        user.setPassword(encodedPassword.trim());

    	user.setAccountNonExpired(true);

		user.setAccountNonLocked(true);

		user.setEnable(true);

		user.setCredentialsNonExpired(true);

		user.setCreateTime(new Date());

		user.setUpdateTime(user.getCreateTime());

		userRepository.saveAndFlush(user);
       	
       	addRoleUser(user,dto.getRoleIdList());
       	
        return user.getId();
	}
    
    
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor=Exception.class)
    public void update(UserEditDto dto)  throws Exception{

       	User oldUser=userRepository.getOne(dto.getId());

		String password=oldUser.getPassword();
		//这里password会被替换掉
		BeanUtils.copyProperties(dto, oldUser);
		//所以重新设置原来的密码
        oldUser.setPassword(password);


		setOtherAttribute(dto, oldUser);

		setOrganization(dto,oldUser);

       	if(!StringUtils.isEmpty(dto.getPassword())) {

       	  String encodedPassword= passwordEncoder.encode(dto.getPassword());

          oldUser.setPassword(encodedPassword.trim());
    	}

		oldUser.setAccountNonExpired(Boolean.valueOf(dto.getAccountNonExpired()));

		oldUser.setAccountNonLocked(Boolean.valueOf(dto.getAccountNonLocked()));

    	updateRoleUser(oldUser,dto.getRoleIdList());

       	userRepository.saveAndFlush(oldUser);

	}

	private void setOtherAttribute(UserDto dto, User t) {
		if(t.getGender()==null) {
			t.setGender(Gender.UNKNOWN);
		}
		if(!StringUtils.isEmpty(dto.getGender())){
			t.setGender(Gender.valueOf(dto.getGender()));
		}
		if(!StringUtils.isEmpty(dto.getCertificateType())){
			t.setCertificateType(CertificateType.valueOf(dto.getCertificateType()));
		}
	}

	@Override
    public Page<User> findByPage(Pageable pageable, UserQueryDto dto) {

     	 return  userRepository.findAll((root, criteriaQuery, criteriaBuilder) -> {

         List<Predicate> predicates = new ArrayList<>();

         if(!StringUtils.isEmpty(dto.getName())){

             predicates.add(criteriaBuilder.like(root.get("name").as(String.class),"%".concat(dto.getName().toLowerCase()).concat("%")));
         }
         
         if(!StringUtils.isEmpty(dto.getRealName())){

             predicates.add(criteriaBuilder.like(root.get("realName").as(String.class),"%".concat(dto.getRealName().toLowerCase()).concat("%")));
         }
         
         if(!StringUtils.isEmpty(dto.getEmail())){

             predicates.add(criteriaBuilder.equal(root.get("email").as(String.class), dto.getEmail()));
         }
         
         if(!StringUtils.isEmpty(dto.getOrganizationName())){

             predicates.add(criteriaBuilder.like(root.get("organization").get("name").as(String.class),"%".concat(dto.getOrganizationName().toLowerCase()).concat("%")));
         }
         
         if(!StringUtils.isEmpty(dto.getPhone())){

             predicates.add(criteriaBuilder.equal(root.get("phone").as(String.class), dto.getPhone()));
         }

		if(!StringUtils.isEmpty(dto.getCertificateNo())){

				 predicates.add(criteriaBuilder.equal(root.get("certificateNo").as(String.class), dto.getCertificateNo()));
		}

		 if(!StringUtils.isEmpty(dto.getCertificateType())){

				 predicates.add(criteriaBuilder.equal(root.get("certificateType").as(CertificateType.class), CertificateType.valueOf(dto.getCertificateType())));
		 }
         
         predicates.add(criteriaBuilder.equal(root.get("deleted").as(Boolean.class),false));

         Predicate[] predicateArray = new Predicate[predicates.size()];

         return criteriaBuilder.and(predicates.toArray(predicateArray));

     }, pageable);


    }
    
	
    @Override
	public Boolean deleteById(Long id){

		User t=userRepository.getOne(id);

		t.setDeleted(true);

		t.setDeletedTime(new Date());

		t.setName(t.getName().concat(DELETED_USER_NAME_FLAG).concat(DateUtils.format(new Date(), DateUtils.FORMAT_DATE_TIME_YYYYMMDDHHMMSS)));

		if(!StringUtils.isEmpty(t.getWechatId())){

			t.setWechatId(t.getWechatId().concat(DELETED_USER_NAME_FLAG).concat(DateUtils.format(new Date(), DateUtils.FORMAT_DATE_TIME_YYYYMMDDHHMMSS)));
		}
		userRepository.saveAndFlush(t);

		return Boolean.TRUE;
	}


	   
	   private  void setOrganization(UserDto dto,User t) {
		   
	     	if(dto.getOrganizationId()!=null){

	      		long organizationId=dto.getOrganizationId();

	      		long organizationTotalCount= organizationRepository.getCountById(organizationId);

	          	if(organizationTotalCount>0L) {

	          		t.setOrganization(organizationRepository.getOne(organizationId));
	          	}
	       	}

	   }
	   
	   
	   /**
	    * 添加用户与角色的关联
	    * @param t
	    * @param roleIds
	    */
       private void addRoleUser(User t,List<Long> roleIds) {

			   for(Long roleId:roleIds) {

				     if(!checkIsRoleIdExist(t.getUserRoles(), roleId)) {
				    	 Role role=roleRepository.getOne(roleId);
					     UserRoleId userRoleId=new UserRoleId();
						 userRoleId.setRoleId(role.getId());
						 userRoleId.setUserId(t.getId());
						 UserRole userRole=new UserRole();
						 userRole.setId(userRoleId);
						 userRole.setUser(t);
						 userRole.setRole(role);
						 t.getUserRoles().add(userRole);
				     }
			   }

		
	   }
       
  
	   /**
	    * 更新用户与角色的关联
	    * @param t
	    * @param roleIdList
	    */
	   private void updateRoleUser(User t,List<Long> roleIdList) {

			if(!roleIdList.isEmpty()){
				userRoleRepository.deleteAllByUserIdAnRoleIdList(t.getId(), roleIdList);
			}else {
				userRoleRepository.deleteAllByUserId(t.getId());
			}
		    addRoleUser(t, roleIdList);
	   }
	   

	   
	   /**
	    * 检是角色ID否存在
	    * @param userRoles
	    * @param roleId
	    * @return
	    */
	   private Boolean checkIsRoleIdExist(Set<UserRole> userRoles,Long roleId) {
    		 for(UserRole ur:userRoles) {
				 if(ur.getRole().getId().longValue()==roleId.longValue()) {
					 return true;
				 }
			 }
    		 return false;
       }
	   @Override
	   public User findByName(String name) {
		  List<User> userList=userRepository.findAllByName(name);
		  return !userList.isEmpty() ?userList.get(0):null;
	   }


	  public List<User> findByRoleName(String name) {
		return userRepository.findAllByRoleName(name);
	  }

	@Override
	public void updatePassword(String newPassword,String userName) {

		User user=findByName(userName);

		String encodedNewPassword= passwordEncoder.encode(newPassword);

		user.setPassword(encodedNewPassword);

		userRepository.saveAndFlush(user);

	}


	@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor=Exception.class)
	public void updateProfile(UserProfileDto dto, String userName){


		User old=findByName(userName);

		BeanUtils.copyProperties(dto,old);

		userRepository.saveAndFlush(old);

	}

	public List<SimpleUser> findAllExceptName(String name,String likeName){

		List<User> userList=null;

		if(!StringUtils.isEmpty(likeName)){
			userList=userRepository.findAllExceptName(name,likeName);
		}else {
			userList=userRepository.findAllExceptName(name);
		}

		return convertToSimpleUsers(userList);

	}



	@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor=Exception.class)
	public void updatePhoto(String photo,String userName) {

		   userRepository.updatePhoto(photo,userName);

	}

	public Boolean isWeChatUserExist(String openId){

	    return !userRepository.findAllByWechatId(openId).isEmpty();

	}

	public List<SimpleUser> findAllByRoleCode(String code){

		List<User> userList=userRepository.findAllByRoleCode(code);

		return convertToSimpleUsers(userList);

	}

	private static List<SimpleUser> convertToSimpleUsers(List<User> userList) {

		List<SimpleUser> simpleUserList=new ArrayList<>();

		for(User user: userList){

			SimpleUser simpleUser=new SimpleUser();

			BeanUtils.copyProperties(user,simpleUser);

			simpleUserList.add(simpleUser);
		}

		return simpleUserList;
	}

}
